/**

   uuid.c
   ======

   UUIDs

**/



/**

   Includes
   --------
	
   - stdlib.h    :  exit, malloc, fopen, fclose, fread, fwrite
   - stdint.h    :  uintX_t
   - stdio.h     :  printf
   - sys/time.h  :  gettimeofday, struct timeval
   - sys/types.h :  open
   - sys/stat.h  :  open
   - fcntl.h     :  open
   - unistd.h    :  close, unlink
   - uuid.h      :  self header

**/


#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "uuid.h"

/**

   Constant: NODE_ID
   -----------------
	
   Node identifier (0-16)
	
**/

#define NODE_ID			1


/**

   Constant: SEQ_FILENAME
   ----------------------
	
   Filename for storing sequence number
	
**/

#define SEQ_FILENAME	"seq"


/**

   Constant: LOCK_FILENAME
   ----------------------
	
   Filename for locking
	
**/

#define LOCK_FILENAME	"lock"





/**

   Structure: struct uuid_state
   ----------------------------
	
   Recorded values for uuid generation.
   Members are:
   - saved_sec  : last second recorded
   - saved_msec : last msecond recorded
   - saved_seq  : last sequence number

**/

struct uuid_state
{
  uint32_t saved_sec;
  uint16_t saved_msec;
  uint8_t saved_seq;
};


/**

   Function: void lock(void)
   -------------------------
	
   Lock by polling a file
	
**/

void lock(void)
{
  int fd;

  while ( (fd = open(LOCK_FILENAME,O_CREAT | O_EXCL)) == -1 )
    {
    }

  close(fd);
  return;
}



/**

   Function: void unlock(void)
   ---------------------------
	
   Unlock by removing a file
	
**/

void unlock(void)
{
  unlink(LOCK_FILENAME);
}


/**

   Function: uint8_t seq_read(struct uuid_state* s)
   ------------------------------------------------
	
   Read uuid state from SEQ_FILENAME
	
**/

uint8_t seq_read(struct uuid_state* s)
{
  FILE* fp;
	
  /* Open file */
  fp = fopen(SEQ_FILENAME,"r");
  if (fp == NULL)
    {
      return EXIT_FAILURE;
    }
	
  /* Read byte */
  if ( fread(s,sizeof(struct uuid_state),1,fp) != 1 )
    {
      fclose(fp);
      return EXIT_FAILURE;
    }
	
  /* Close and return */
  fclose(fp);
  return EXIT_SUCCESS;
}


/**

   Function: uint8_t seq_write(struct uuid_state* s)
   -------------------------------------------------
	
   Write uuid state to SEQ_FILENAME
	
**/

uint8_t seq_write(struct uuid_state* s)
{
  FILE* fp;
	
  /* Open file */
  fp = fopen(SEQ_FILENAME,"w");
  if (fp == NULL)
    {
      return EXIT_FAILURE;
    }
	
  /* Read byte */
  if ( fwrite(s,sizeof(struct uuid_state),1,fp) != 1 )
    {
      fclose(fp);
      return EXIT_FAILURE;
    }
	
  /* Close and return */
  fclose(fp);
  return EXIT_SUCCESS;
}


/**

   Function: struct uuid* uuid_create(void)
   ----------------------------------------
	
   Create a 64-bits uuid
	
**/


struct uuid* uuid_create(void)
{
  struct timeval tv;
  struct uuid* uuid;
  struct uuid_state state;
  uint8_t rnd,id;
	
  /* Allocate uuid */
  uuid = (struct uuid*)malloc(sizeof(struct uuid));
  if (uuid == NULL)
    {
      return NULL;
    }
	
  /* Get system time */
  if (gettimeofday(&tv,(struct timezone*)0) != 0)
    {
      goto err;
    }
	
  uuid->sec = tv.tv_sec;
  uuid->msec = tv.tv_usec >> 10;
	
  /* Generate random */
  srand((unsigned int)(tv.tv_usec ^ tv.tv_sec));
  rnd = (uint8_t)(rand());

  /* Get id */
  id = (uint8_t)NODE_ID;
	
  /* Set node_rand field */
  uuid->node_rand = 0;
  uuid->node_rand |= rnd;
  uuid->node_rand |= (id<<4);
	
  /* Lock */
  lock();
	
  /* Read Sequence */
  if ( seq_read(&state) != EXIT_SUCCESS )
    {
      /* Write new sequence if file does not exist */
      state.saved_sec = uuid->sec;
      state.saved_msec = uuid->msec;
      state.saved_seq = rnd;
      if ( seq_write(&state) != EXIT_SUCCESS )
	{
	  /* Storage dead maybe */
	  goto err;
	}
    }
	
  /* Set sequence number */
  uuid->seq = state.saved_seq;
	
  /* New sequence if time has gone backward */
  if ( (uuid->sec < state.saved_sec)
       || ( (uuid->sec == state.saved_sec)&&(uuid->msec <= state.saved_msec) ) )
    {
      /* Increment sequence number */
      uuid->seq++;
    }
	
	
  /* Save state */
  state.saved_sec = uuid->sec;
  state.saved_msec = uuid->msec;
  state.saved_seq = uuid->seq;
  if ( seq_write(&state) != EXIT_SUCCESS )
    {
      goto err;
    }
	
  /* Unlock */
  unlock();
	
  /* return uuid */
  return uuid;
	
 err:
	
  free(uuid);
  return NULL;

}


/**

   Function: int uuid_compare(struct uuid* u1, struct uuid* u2)
   ------------------------------------------------------------
	
   Compare 2 uuids
	
**/

int uuid_compare(struct uuid* u1, struct uuid* u2)
{
  if ( u1->sec != u2->sec )
    {
      return (u1->sec - u2->sec);
    }
  else
    {
      if ( u1->msec != u2->msec )
	{
	  return (u1->sec - u2->sec);
	}
      else
	{
	  if ( u1->seq != u2->seq )
	    {
	      return (u1->seq - u2->seq);
	    }
	  else
	    {
	      return (u1->node_rand - u2->node_rand);
	    }
	}
    }
}


/**

   Function: void uuid_delete(struct uuid* uuid)
   ---------------------------------------------
	
   Release an uuid
   
**/

void uuid_delete(struct uuid* uuid)
{
  free(uuid);
  return;
}
